/**
 * Copyright (C) 2010 altuure <altuure [AT] gmail [DOT] com> http://www.altuure.com/projects/yagdao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.altuure.yagdao.jpa;


import com.altuure.yagdao.YOperator;
import com.altuure.yagdao.common.YagdaoException;
import com.altuure.yagdao.grammar.JpaTreeWalker;
import com.altuure.yagdao.grammar.YagdaoLexer;
import com.altuure.yagdao.grammar.YagdaoParser;
import com.altuure.yagdao.grammar.YagdaoParser.*;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.RecognizerSharedState;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;

import javax.persistence.criteria.*;
import java.util.List;
import java.util.Map;

public class JpaGrammarHelper {
   
    public static SelectList getSelectionList(String input, Root from, CriteriaBuilder criteriaBuilder,
            Map<String, From> joins) {
        try {
            ANTLRStringStream stringStream = new ANTLRStringStream(input);
            final YagdaoLexer lexer = new YagdaoLexer(stringStream);
            YagdaoParser parser = new YagdaoParser(new CommonTokenStream(lexer));

            result_list_return result_list = parser.result_list();
            List<String> errors = parser.getErrors();

            if (!errors.isEmpty()) {
                StringBuffer sb = new StringBuffer();
                for (String string : errors) {
                    sb.append(string).append(",");
                }
                throw new IllegalArgumentException("ParseError:" + sb.toString());
            }

            CommonTree tree = (CommonTree) result_list.getTree();

            CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
            RecognizerSharedState state = new RecognizerSharedState();
            state.backtracking = 0;
            JpaTreeWalker evaluator = new JpaTreeWalker(nodes, state);
            evaluator.init(from, joins, criteriaBuilder);
            com.altuure.yagdao.grammar.JpaTreeWalker.result_list_return list_return = evaluator.result_list();
            List l = list_return.list;
            boolean distinct=list_return.distinct;
            
            
            errors = evaluator.getErrors();
            for (String string : errors) {
                System.err.println("ERROR:" + string);
            }

            return new SelectList(l,distinct);
        } catch (RecognitionException e) {
            throw new YagdaoException("possible parse Exception", e);
        }

    }
    public static PredicateResult addPredicate(String conditionText, Root from, CriteriaBuilder criteriaBuilder,
            Map<String, From> joins,Object value, YOperator operator) {
        
        try {
            ANTLRStringStream stringStream = new ANTLRStringStream(conditionText);
            final YagdaoLexer lexer = new YagdaoLexer(stringStream);
            YagdaoParser parser = new YagdaoParser(new CommonTokenStream(lexer));
            
             condition_col_return parserResult = parser.condition_col();
            List<String> errors = parser.getErrors();
            
            if (!errors.isEmpty()) {
                StringBuffer sb = new StringBuffer();
                for (String string : errors) {
                    sb.append(string).append(",");
                }
                throw new YagdaoException("ParseError:" + sb.toString());
            }
            
            CommonTree tree = (CommonTree) parserResult.getTree();
            
            CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
            RecognizerSharedState state = new RecognizerSharedState();
            state.backtracking = 0;
            JpaTreeWalker evaluator = new JpaTreeWalker(nodes, state);
            evaluator.init(from, joins, criteriaBuilder);
            evaluator.setValue(value);
            evaluator.setDefaultOp(operator);
            com.altuure.yagdao.grammar.JpaTreeWalker.condition_col_return condition_col = evaluator.condition_col();
            Predicate predicate = condition_col.predicate;
            boolean hasHaving = condition_col.hasHaving;
            
            errors = evaluator.getErrors();
            
            if (!errors.isEmpty()) {
                StringBuffer sb = new StringBuffer();
                for (String string : errors) {
                    sb.append(string).append(",");
                }
                throw new YagdaoException("ParseError:" + sb.toString());
            }
            return new PredicateResult(predicate, hasHaving);
          
        } catch (RecognitionException e) {
            throw new YagdaoException("possible parse Exception", e);
        }
        
    }
    public static List<Expression> getGroupsByStatements(String groupBy, Root from, CriteriaBuilder criteriaBuilder,
            Map<String, From> joins) {
        
        try {
            ANTLRStringStream stringStream = new ANTLRStringStream(groupBy);
            final YagdaoLexer lexer = new YagdaoLexer(stringStream);
            YagdaoParser parser = new YagdaoParser(new CommonTokenStream(lexer));
            
            group_col_return parserResult = parser.group_col();
            List<String> errors = parser.getErrors();
            
            if (!errors.isEmpty()) {
                StringBuffer sb = new StringBuffer();
                for (String string : errors) {
                    sb.append(string).append(",");
                }
                throw new YagdaoException("ParseError:" + sb.toString());
            }
            
            CommonTree tree = (CommonTree) parserResult.getTree();
            
            CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
            RecognizerSharedState state = new RecognizerSharedState();
            state.backtracking = 0;
            JpaTreeWalker evaluator = new JpaTreeWalker(nodes, state);
            evaluator.init(from, joins, criteriaBuilder);

            List<Expression> predicates = evaluator.group_col().predicates;
            
            
            errors = evaluator.getErrors();
            
            if (!errors.isEmpty()) {
                StringBuffer sb = new StringBuffer();
                for (String string : errors) {
                    sb.append(string).append(",");
                }
                throw new YagdaoException("ParseError:" + sb.toString());
            }
            return predicates;
            
        } catch (RecognitionException e) {
            throw new YagdaoException("possible parse Exception", e);
        }
        
    }
    
    public static Predicate havingStatements(String having, Root from, CriteriaBuilder criteriaBuilder,
            Map<String, From> joins) {
        
        try {
            ANTLRStringStream stringStream = new ANTLRStringStream(having);
            final YagdaoLexer lexer = new YagdaoLexer(stringStream);
            YagdaoParser parser = new YagdaoParser(new CommonTokenStream(lexer));
            
             having_col_return parserResult = parser.having_col();
            List<String> errors = parser.getErrors();
            
            if (!errors.isEmpty()) {
                StringBuffer sb = new StringBuffer();
                for (String string : errors) {
                    sb.append(string).append(",");
                }
                throw new YagdaoException("ParseError:" + sb.toString());
            }
            
            CommonTree tree = (CommonTree) parserResult.getTree();
            
            CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
            RecognizerSharedState state = new RecognizerSharedState();
            state.backtracking = 0;
            JpaTreeWalker evaluator = new JpaTreeWalker(nodes, state);
            evaluator.init(from, joins, criteriaBuilder);
           
             Predicate predicate = evaluator.having_col().predicate;
            
            
            errors = evaluator.getErrors();
            
            if (!errors.isEmpty()) {
                StringBuffer sb = new StringBuffer();
                for (String string : errors) {
                    sb.append(string).append(",");
                }
                throw new YagdaoException("ParseError:" + sb.toString());
            }
            return predicate;
            
        } catch (RecognitionException e) {
            throw new YagdaoException("possible parse Exception", e);
        }
        
    }
    public static Predicate whereStatements(String having, Root from, CriteriaBuilder criteriaBuilder,
            Map<String, From> joins) {
        
        try {
            ANTLRStringStream stringStream = new ANTLRStringStream(having);
            final YagdaoLexer lexer = new YagdaoLexer(stringStream);
            YagdaoParser parser = new YagdaoParser(new CommonTokenStream(lexer));
            
             where_clause_return parserResult = parser.where_clause();
            List<String> errors = parser.getErrors();
            
            if (!errors.isEmpty()) {
                StringBuffer sb = new StringBuffer();
                for (String string : errors) {
                    sb.append(string).append(",");
                }
                throw new YagdaoException("ParseError:" + sb.toString());
            }
            
            CommonTree tree = (CommonTree) parserResult.getTree();
            
            CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
            RecognizerSharedState state = new RecognizerSharedState();
            state.backtracking = 0;
            JpaTreeWalker evaluator = new JpaTreeWalker(nodes, state);
            evaluator.init(from, joins, criteriaBuilder);
          
            Predicate predicate = evaluator.where_clause().predicate;
            
            
            errors = evaluator.getErrors();
            
            if (!errors.isEmpty()) {
                StringBuffer sb = new StringBuffer();
                for (String string : errors) {
                    sb.append(string).append(",");
                }
                throw new YagdaoException("ParseError:" + sb.toString());
            }
            return predicate;
            
        } catch (RecognitionException e) {
            throw new YagdaoException("possible parse Exception", e);
        }
        
    }
    
    public static class PredicateResult{
        private Predicate predicate;
        private boolean isHaving;
        public PredicateResult(Predicate predicate, boolean isHaving) {
            super();
            this.predicate = predicate;
            this.isHaving = isHaving;
        }
        /**
         * @return the predicate
         */
        public Predicate getPredicate() {
            return predicate;
        }
        /**
         * @return the isHaving
         */
        public boolean isHaving() {
            return isHaving;
        }
        
        
    }
    public static class SelectList{
        private List<Expression> expressions;
        private boolean distinct;
        public SelectList(List<Expression> expressions, boolean distinct) {
            super();
            this.expressions = expressions;
            this.distinct = distinct;
        }
        /**
         * @return the expressions
         */
        public List<Expression> getExpressions() {
            return expressions;
        }
        /**
         * @return the distinct
         */
        public boolean isDistinct() {
            return distinct;
        }
        
    }
}
